package controller;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import model.Casilla;
import model.CentroDeComandos;
import model.Comodin;
import model.Jugada;
import model.JugadaGanadora;
import model.JugadaPerdedora;
import model.Premio;
import model.Rodillo;
import model.Simple;
import model.interfaces.Observer;
import model.view.CasillaView;
import persistencia.AdmPersistenciaCasilla;
import persistencia.AdmPersistenciaMaquina;
import persistencia.AdmPersistenciaPremio;
import persistencia.AdmPersistenciaRodillo;
import controller.exception.MaquinaInstantiationException;
import controller.interfaces.Observable;


public class Maquina implements Observable{
	private int codigo;
	private float recaudacion;
	private float recaudacionMinima;
	private float credito;
	private float precioJugada;
	private Set<Premio> premios; 
	private Set<Casilla> casillas;
	private List<Rodillo> rodillos;
	private static Maquina instancia;
	private List<Observer> observers;
	private List<Jugada> jugadas;
	
	public static Maquina getInstancia() throws MaquinaInstantiationException
	{
		if (instancia==null){
			instancia=AdmPersistenciaMaquina.getInstancia().selectMaquina();
			if (instancia==null){
				throw new MaquinaInstantiationException("No hay una instancia de Maquina creada, se debe crear una en la pantalla de crear maquina");
			}
			
		}
		return instancia;
	}
	
	
	

	public Maquina(float recaudacion2, float recaudacionMinima2,
			float precioJugada2) throws MaquinaInstantiationException{
		if (instancia==null){
			this.recaudacion=recaudacion2;
			this.recaudacionMinima=recaudacionMinima2;
			this.precioJugada=precioJugada2;
			this.rodillos = new ArrayList<Rodillo>();
			AdmPersistenciaMaquina.getInstancia().insertarMaquina(this);
			instancia=this;
		}	else	{
			throw new MaquinaInstantiationException("Ya hay una instancia de Maquina creada");
		}
	}
	



	
	public Maquina(int codigo2, float recaudacion2, float recaudacionMinima2,
			float precioJugada2,float credito)throws MaquinaInstantiationException {
		if (instancia==null){
			this.codigo=codigo2;
			this.credito=credito;
			this.recaudacion=recaudacion2;
			this.recaudacionMinima=recaudacionMinima2;
			this.precioJugada=precioJugada2;
			this.rodillos = new ArrayList<Rodillo>();
			this.observers= new ArrayList<Observer>();
			this.observers.add(new CentroDeComandos());
		}	else	{
			throw new MaquinaInstantiationException("Ya hay una instancia de Maquina creada");
		}
	}






	private Set<CasillaView> createListView(Set<Casilla> casillas) {
		Set<CasillaView> casillasView=new HashSet<CasillaView>();
		for (Casilla casilla: casillas){
			casillasView.add(casilla.getView());
		}
		return casillasView;
	}
	
	public boolean crearCasillaSimple(String fruta ){
		boolean insert=false;
		try{
			new Simple(fruta);
			insert=true;
		}catch (Exception e){
			
		}
		return insert;
	}
	
	public boolean crearCasillaComodin(List<Integer> frutasId,String nombre ){
		boolean insert=false;
		try{
			List<Casilla> simples=crearFrutasSimplesParaComodin(frutasId);
			new Comodin(simples,nombre);
			insert=true;
		}catch (Exception e){
			
		}
		return insert;
	}
	
	private List<Casilla> crearFrutasSimplesParaComodin(List<Integer> frutasId){
		List<Casilla> simples=new ArrayList<Casilla>();
		for (Integer frutaId:frutasId){
			simples.add(new Simple(frutaId));
		}
		return simples;
	}

	public boolean crearPremio(List<Integer> frutasId,Float Monto){
		boolean insert=false;
		try{
			List<Casilla> casillasPremio=AdmPersistenciaCasilla.getInstancia().getCasillas(frutasId);
			new Premio(casillasPremio,Monto);
			insert=true;
		}catch (Exception e){
			
		}
		
		return insert;
	}
	
	public Integer obtenerCantidadRodillos(){
		return Rodillo.getCantidadRodillos();
	}
	
	public int getCodigo() {
		return codigo;
	}


	public float getCredito() {
		return credito;
	}


	public Set<Premio> getPremios() {
		if (premios==null || premios.isEmpty()){
			if (premios==null)premios=new HashSet<Premio>();
			premios.addAll(AdmPersistenciaPremio.getInstancia().selectPremios());
		}
		return premios;
	}



	public float getRecaudacion() {
		return recaudacion;
	}


	public void setRecaudacion(float recaudacion) {
		this.recaudacion = recaudacion;
	}


	public float getRecaudacionMinima() {
		return recaudacionMinima;
	}


	public void setRecaudacionMinima(float recaudacionMinima) {
		this.recaudacionMinima = recaudacionMinima;
	}



	public float getPrecioJugada() {
		return precioJugada;
	}


	public void setPrecioJugada(float precioJugada) {
		this.precioJugada = precioJugada;
	}


	public Set<Casilla> getCasillas() {
		casillas=AdmPersistenciaCasilla.getInstancia().selectCasillas();
		return casillas;
	}

	public Set<CasillaView> obtenerCasillas(){
		return createListView(getCasillas());
	}


	public void setCasillas(Set<Casilla> casillas) {
		this.casillas = casillas;
	}
	
	public List<Rodillo> getRodillos() {
		if (rodillos==null || rodillos.isEmpty()){
			rodillos=AdmPersistenciaRodillo.getInstancia().selectRodillos();
		}
		return rodillos;
	}
	
	public void setRodillos(List<Rodillo> rodillos) {
		this.rodillos = rodillos;
	}





	public void actualizarCredito(Float credito) {
		this.credito=this.credito+credito;
		
	}
	
	/**
	 * Closes the computer in the current state 
	 */
	public void cerrarSesion(){
		AdmPersistenciaMaquina.getInstancia().guardarEstadoMaquina(this);
	}

	public boolean crearRodillo(List<Integer> idsCasilla) {
		List<Casilla> casillasEnRodillo = new ArrayList<Casilla>();
		for (Casilla casilla : this.casillas) {
			for (int idCasilla : idsCasilla) {
				if (idCasilla == casilla.getCodigo()) {
					casillasEnRodillo.add(casilla);
				}
			}
		}
		this.rodillos.add(new Rodillo(casillasEnRodillo));
		boolean success=true;
		return success; 
	}
	
	public boolean formatearMaquina(){
		boolean success = Premio.formatearPremios() && Rodillo.formatearRodillos() && Jugada.formatearJugadas() && Casilla.formatearCasillas() && AdmPersistenciaMaquina.getInstancia().delete();
		return success;
		
		
	}
	

	/**
	 * Metodo para girar rodillos y jugar.
	 * @return boolean true cuando hay premio. Si hay premio, debera ser aceptado luego
	 */
	public boolean jugar(){
		Premio premioGanador=null;
		if(validarCredito()){
			cobrarJugada();
			List<Casilla> resultado=girarRodillos();
			for (Premio premio:getPremios()){
				if (premio.esGanador(resultado)){
					premioGanador=premio;
					break;
				}
			}
			if (premioGanador!=null){
				recaudacion=recaudacion-premioGanador.getMonto();
				credito=credito+premioGanador.getMonto();
				new JugadaGanadora(this.recaudacion, this.credito, resultado, premioGanador);
			}else{
				new JugadaPerdedora(this.recaudacion, this.credito, resultado);
			}

		}
		
		return premioGanador!=null;
	}



	private List<Casilla> girarRodillos() {
		List<Casilla> resultado=new ArrayList<Casilla>();
		for (Rodillo rodillo:getRodillos()) {
			resultado.add(rodillo.girar());
		}
		return resultado;
	}
	

	
	

	/**
	 * Aceptar premio
	 * @return float 
	 */
	public Float aceptarPremio(){
		notifyObservers();
		return credito;
	}



	private void cobrarJugada() {
		this.credito=credito-this.precioJugada;
	}




	private boolean validarCredito() {
		return this.credito>=this.precioJugada;
		
	}




	public void addObserver(Observer obs) {
		observers.add(obs);
		
	}




	public void deleteObserver(Observer obs) {
		observers.remove(obs);
	}




	public void notifyObservers() {
		for (Observer obs: observers){
			obs.notify(recaudacion, credito);
		}
		
	}




	
	
	
	
	
}
